package com.leyou.search.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.JsonUtils;
import com.leyou.common.vo.PageResult;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.*;
import com.leyou.search.dto.GoodsDTO;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.similarity.ScriptedSimilarity;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SourceFilter;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务层代码
 */
@Service
public class SearchService {

    @Autowired
    private ItemClient itemClient;

    public Goods buildGoods(SpuDTO spu){
        // spu的id
        Long spuId = spu.getId();
        //====================================================
        // 1、构建sku集合
        List<Map<String, Object>> skuDTOList = new ArrayList<>();
        // 1.1 遍历集合
        List<SkuDTO> skuDTOS = itemClient.querySkuListBySpuId(spuId);
        skuDTOS.forEach(sku->{
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("id", sku.getId());
            map.put("title", sku.getTitle());
            map.put("price", sku.getPrice());
            map.put("image", StringUtils.substringBefore(sku.getImages(), ","));
            skuDTOList.add(map);
        });
        // 1.2 把list集合转成json字符串
        String skus = JsonUtils.toString(skuDTOList);

        // 2、 price 价格set集合
        Set<Long> priceSet = skuDTOS.stream().map(SkuDTO::getPrice).collect(Collectors.toSet());

        // 3、all 字段处理:
        // 3.1 分类名称
        List<CategoryDTO> categoryDTOList = itemClient.queryCategoryByIds(spu.getCategoryIds());
        String categoryNames = categoryDTOList.stream().map(CategoryDTO::getName).collect(Collectors.joining());
        // 3.2 品牌名称
        BrandDTO barndDTO = itemClient.queryBrandById(spu.getBrandId());
        // 3.3 拼接字符串
        String all = spu.getName() + categoryNames + barndDTO.getName();

        // 4、specs  规格参数处理： map
        // 4.1 准备map的key： 通用规格参数
        SpuDetailDTO spuDetail = itemClient.querySpuDetailBySpuId(spuId);
        Map<Long, Object> genericSpecMap = JsonUtils.toMap(spuDetail.getGenericSpec(), Long.class, Object.class);
        // 4.2 准备mapkey：  特有规格参数
        Map<Long, List<String>> specialSpecMap = JsonUtils.nativeRead(spuDetail.getSpecialSpec(), new TypeReference<Map<Long, List<String>>>() {
        });
        // 4.3 查询规格参数
        List<SpecParamDTO> specParamDTOS = itemClient.querySpecParams(null, spu.getCid3(), true);
        // 4.4 遍历规格参数
        Map<String, Object> specsMap = new HashMap<>();
        specParamDTOS.forEach(specParamDTO -> {
            String key = specParamDTO.getName();
            Object value = null;
            if(specParamDTO.getGeneric()){
                // 通用
                value = genericSpecMap.get(specParamDTO.getId());
            }else{
                // 特有
                value = specialSpecMap.get(specParamDTO.getId());
            }

            // 处理数字
            if(specParamDTO.getNumeric()){
                // 把数字转成区间
                value = chooseSegment(value, specParamDTO);
            }
            specsMap.put(key, value);
        });
        //====================================================
        // 创建goods对象
        Goods goods = new Goods();
        goods.setId(spuId); // id
        goods.setSubTitle(spu.getSubTitle()); // 买点
        goods.setBrandId(spu.getBrandId()); // 品牌的id
        goods.setCategoryId(spu.getCid3()); // 三级分类id
        goods.setCreateTime(spu.getCreateTime().getTime());// 创建时间
        goods.setSkus(skus); // sku集合处理
        goods.setPrice(priceSet); // 价格set集合
        goods.setAll(all); //查询的字段，包含所有需要查询的内容
        goods.setSpecs(specsMap); //规格参数

        // 返回值
        return goods;
    }



    private String chooseSegment(Object value, SpecParamDTO p) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }


    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    /**
     * 查询商品数据
     * @param request
     * @return
     */
    public PageResult<GoodsDTO> queryGoodByPage(SearchRequest request) {
        // 封装查询条件
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        // 过滤我们需要的字段
        query.withSourceFilter(new FetchSourceFilter(new String[]{"id","subTitle","skus"},null));
        // 分页的条件
        query.withPageable(PageRequest.of(request.getPage()-1, request.getSize()));
        // 过滤的条件:  match查询会分词
        query.withQuery(buildSearchParam(request));

        // 执行查询
        AggregatedPage<Goods> goodsResult = elasticsearchTemplate.queryForPage(query.build(), Goods.class);

        // 总记录数
        long totalElements = goodsResult.getTotalElements();
        // 总页数
        int totalPages = goodsResult.getTotalPages();
        // 当前页数据
        List<Goods> content = goodsResult.getContent();

        //返回值
        return new PageResult(totalElements,totalPages, BeanHelper.copyWithCollection(content, GoodsDTO.class));
    }

    /**
     * 拼接查询条件
     * @param request
     * @return
     */
    private QueryBuilder buildSearchParam(SearchRequest request) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 第一个搜索条件
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND);
        boolQueryBuilder.must(matchQueryBuilder);
        // 第二个过滤的条件
        request.getFilters().entrySet().forEach(entry -> {
            String key = entry.getKey();
            Object value = entry.getValue();
            String field = ""; // 查询的域字段名称
            if("分类".equals(key)){
                field = "categoryId";
            }else if("品牌".equals(key)){
                field = "brandId";
            }else{
                // 规格参数
                field = "specs."+key;
                System.out.println(" 过滤参数=====> "+field);
            }
            boolQueryBuilder.filter(QueryBuilders.termsQuery(field,value));
        });
        return boolQueryBuilder;
    }

    /**
     * 查询过滤参数
     * @param request
     * @return
     */
    public Map<String, List<?>> queryFilterParams(SearchRequest request) {
        // 存入过滤参数:  需要有序的
        Map<String, List<?>> paramsMap = new LinkedHashMap<>();
        // 聚合查询
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 不查询内容
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
        // 分页:  SpringDataElasticSearch  的  size必须大于等于1
        queryBuilder.withPageable(PageRequest.of(0,1));
        // 过滤的条件:  match查询会分词
        queryBuilder.withQuery(buildSearchParam(request));
        // =====================================================================================================
        // 添加聚合的条件： 对分类id聚合
        String categoryIdAgg = "categoryIdAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryIdAgg).field("categoryId"));

        // 添加聚合的条件： 对品牌id聚合
        String brandIdAgg = "brandIdAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(brandIdAgg).field("brandId").size(Integer.MAX_VALUE));
        // =====================================================================================================
        // 查询聚合结果
        AggregatedPage<Goods> goodsAgg = elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);
        //获取结果
        // 处理分类的聚合
        Terms categoryTerm = (Terms) goodsAgg.getAggregation(categoryIdAgg);
        List<Long> categoryIds = handlerCategoryTerm(categoryTerm, paramsMap);
        // 处理品牌的聚合
        Terms brandTerm = goodsAgg.getAggregations().get(brandIdAgg);
        handlerBrandTerm(brandTerm, paramsMap);

        //根据分类id的集合处理规格参数：categoryIds
        /**
         * 参数一： 查询的条件
         * 参数二： 分类id集合
         * 参数三： paramsMap
         */
        addSpecParmsByCategoryIds(request,categoryIds,paramsMap);

        // 返回的内容
        return paramsMap;
    }

    /**
     * 参数一： 查询的条件
     * 参数二： 分类id集合
     * 参数三： paramsMap
     */
    private void addSpecParmsByCategoryIds(SearchRequest request, List<Long> categoryIds, Map<String, List<?>> paramsMap) {
        if(!CollectionUtils.isEmpty(categoryIds)){
            categoryIds.forEach(cid -> {
                // 聚合查询
                NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
                // 不查询内容
                queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
                // 分页:  SpringDataElasticSearch  的  size必须大于等于1
                queryBuilder.withPageable(PageRequest.of(0,1));
                // 过滤的条件:  match查询会分词
                queryBuilder.withQuery(buildSearchParam(request));

                // 根据分类id查询规格参数
                List<SpecParamDTO> specParamDTOS = itemClient.querySpecParams(null, cid, true);

                // 拼接聚合条件
                for (SpecParamDTO specParamDTO : specParamDTOS) {
                    String specName = specParamDTO.getName();
                    queryBuilder.addAggregation(AggregationBuilders.terms(specName).field("specs."+specName));
                }

                //查询
                AggregatedPage<Goods> goodsAgg = elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);
                // 获取结果
                for (SpecParamDTO specParamDTO : specParamDTOS) {
                    // 拼接聚合条件
                    String specName = specParamDTO.getName();
                    Terms specPramTerm = goodsAgg.getAggregations().get(specName);
                    List<String> spceList = specPramTerm.getBuckets().stream()
                            .map(Terms.Bucket::getKeyAsString)
                            .filter(StringUtils::isNotBlank)
                            .collect(Collectors.toList());
                    paramsMap.put(specName, spceList);
                }

            });
        }
    }

    /**
     处理分类
            1、往map存入分类
            2、返回分类id的集合：用于查询规格过滤参数
     */
    private List<Long> handlerCategoryTerm(Terms categoryTerm, Map<String, List<?>> paramsMap) {
        // 获取聚合后的分类id集合
        List<Long> categoryIds = categoryTerm.getBuckets().stream()
                .map(Terms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());
        // 去查询
        List<CategoryDTO> categoryDTOList = itemClient.queryCategoryByIds(categoryIds);
        // 往map中存入
        paramsMap.put("分类", categoryDTOList);

        // 返回
        return categoryIds;
    }

    // 处理品牌
    private void handlerBrandTerm(Terms brandTerm, Map<String, List<?>> paramsMap) {
        List<Long> brandIds = brandTerm.getBuckets().stream()
                .map(Terms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());

        List<BrandDTO> brandDTOS = itemClient.queryBrandByIds(brandIds);
        paramsMap.put("品牌", brandDTOS);
    }


    // 创建索引
    public void createItemIndex(Long id) {
        SpuDTO spu = itemClient.querySpuById(id);  // 根据id查询spu
        Goods goods = buildGoods(spu); // 创建goods对象
        goodsRepository.save(goods); // 创建索引库对象
    }

    // 删除索引
    public void deleteItemIndex(Long id) {
        goodsRepository.deleteById(id);
    }
}
